#
# Copyright (C) 2008 Jiakuan Wang, All rights reserved.
# 
# @author: Jiakuan Wang <delight.wjk@gmail.com>
#

"""This script uses pygettext.py to generate all messages to PO files. It 
could be used to regenerate PO files at any time while developing application.

The following describes what this script actually do step by step:

 1. Check if there original generated po files, if there are po files generated
    before by the script, they should be separated by the language tag in the 
    file names, that is, every language has one po file for the application. 
    Now read the po files and save the old messages of the po files into 
    several python dictionaries according to different languages. These 
    dictionaries will be used in the fourth step of this script. 

 2. Since there might be a length limitation on system command on some 
    operating systems, so if there are many python files in the application, 
    the pygettext.py script, which extracts messages from python files, will be
    failed when we execute it because the command line is too long.
    
    To solve this problem, we can merge all the python files of the application 
    into one temporary file, and then use the pygettext.py script to extract
    messages from this temporary file, and finally delete the temporary file.
    
    So in this step we just walk through the directory tree from the base 
    directory of the application, and read every python file in every directory 
    and subdirectory in the directory tree and append the content of every 
    python file to the global temporary file.

 3. From the temporary python file generated in the previous step, we extract 
    messages into po files by pygettext.py script according to different 
    languages. After that, delete the temporary file.

 4. Check the message dictionaries generated in the first step to find out if 
    there are translated messages for the messages in the new PO files, and 
    merge the translated messages into the new PO files according to different
    languages.

To use this script, just copy this file to the base directory of the application.
"""

import os
import i18n

# The base directory which contains the application files that need to be 
# internationalized, default it is the same directory of this script.
baseDir = os.path.dirname(__file__)

# Temporary python file
tempPyFile = os.path.join(baseDir, '_temp.py')

# Excluded python files which will not be merged to the temporary file.
excludedPyFiles = ['_temp.py', 'i18n.py', 'py2po.py', 'po2mo.py', 
                   'msgfmt.py', 'pygettext.py', '__init__.py']

# Supported languages
languages = ('en', 'zh_CN', 'zh_TW')

# Old message dictionary, include all languages, follow this format: 
# msgstr = oldMsgDict[language][msgid]
oldMsgDictOfAllLanguages = {}
for language in languages:
    oldMsgDictOfAllLanguages[language] = ''

def getPoFile(language):
    "Get the PO file name by specified language."

    return i18n.domain + '_' + language + '.po'

def buildMsgDict(language):
    '''Build message dictionary from PO file. The following is the example of 
    a PO file. 
        
    -----------------------------------------------------------------
    # PO File Generated by py2po.py script.
    # Copyright (C) 2008 Widen
    # Jiakuan Wang <delight.wjk@gmail.com>, 2008.
    #
    msgid ""
    msgstr ""
    "Project-Id-Version: PACKAGE VERSION\n"
    "POT-Creation-Date: 2008-08-07 00:15\n"
    "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    "Language-Team: LANGUAGE <LL@li.org>\n"
    "MIME-Version: 1.0\n"
    "Content-Type: text/plain; charset=utf-8\n"
    "Content-Transfer-Encoding: ENCODING\n"
    "Generated-By: pygettext.py 1.5\n"
    
    
    #: source.py:19
    msgid "Javava Task"
    msgstr ""
    
    #: source.py:39
    msgid "&File"
    msgstr ""
    
    #: source.py:41
    msgid "&Options..."
    msgstr ""
    
    #: source.py:73
    msgid ""
    "Choose a preferred language, it will be valid after \n"
    "restarting application."
    msgstr ""
    -----------------------------------------------------------------
    
    Regarding the PO format above, either the value of the 'msgid' or the 
    'msgstr' may be multiple line value. This method parses this type of file
    in to msgid & msgstr pairs, and return them as a python dictionary.
    '''
    
    MSG_ID = 'msgid'
    MSG_STR = 'msgstr'

    # This variable will store the msgid and msgstr pairs and to be returned.
    msgDict = {}
    
    # Store the value of each msgid temporarily.
    msgid = ''
    
    # Store the value of each msgstr temporarily.
    msgstr = ''
    
    # Record the current processing content for the processor, this variable
    # will let the loop know what the content type of the current line. The 
    # values of this variable should be 'msgid' or 'msgstr'.
    currProcessing = ''
    
    poFile = open(getPoFile(language))
    
    # Deal with each line of the PO file
    for line in poFile:
        if line:            
            # If the line is not empty, its prefix may be one of four types:
            #   1. Start with '#' - it means that is a comment line, skip it.
            #   2. Start with 'msgid' - it means we begin to process a msgid.
            #   3. Start with 'msgstr' - it means we begin to process a msgstr.
            #   4. Start with '"' - it means this line is pure content for 
            #      msgid or msgstr, it is subject to the currProcessing 
            #      declared above.
            if line.startswith('#'):
                continue

            elif line.startswith(MSG_ID):
                # Check if the msgid is empty or not, if it is not empty, put 
                # the values msgid and msgstr into dictionary.
                if msgid:
                    msgDict[msgid] = msgstr
                
                # Ready to process new msgid.
                currProcessing = MSG_ID
                msgid = line[len(MSG_ID)+1:]

            elif line.startswith(MSG_STR):
                currProcessing = MSG_STR
                msgstr = line[len(MSG_STR)+1:]

            elif line.startswith('"'):
                # This line is pure content, if the currProcess is msgid, then
                # append this line to the content of msgid, otherwise append it 
                # to the content of msgstr.
                if currProcessing == MSG_ID:
                    msgid += line
                elif currProcessing == MSG_STR:
                    msgstr += line

    # Deal the last msgid & msgstr pair of the PO file
    if msgid:
        msgDict[msgid] = msgstr
    
    # After visited all lines in the PO file, close it.
    poFile.close()
    
    # Return the dictionary.
    return msgDict

def checkOldPoFiles():
    "Check the old PO files generated before."

    for language in languages:
        poFile = getPoFile(language)
        if os.path.exists(poFile):
            print 'Find old file:', os.path.abspath(poFile)
            oldMsgDictOfAllLanguages[language] = buildMsgDict(language)

def mergePyFiles():
    "Merage all python files to one temporary file."
    
    # Initialize the temporary file.
    tempFile = open(tempPyFile, 'w')
    tempFile.write('')
    tempFile.close()
    
    # Open temporary python file as append mode.
    tempFile = open(tempPyFile, 'a')
    os.path.walk(baseDir, pyFileVisitor, tempFile)
    tempFile.close()

def pyFileVisitor(tempFile, dirName, filesHere):
    "Append the content of every python file to the temporary python file."
    
    for fileName in filesHere:
        if fileName.endswith('.py'):
            # Don't merge the excluded py files.
            if fileName in excludedPyFiles:
                continue
            
            pathName = os.path.join(dirName, fileName)
            print 'Merge the content of file:', pathName
            pyFile = open(pathName)
            tempFile.write(pyFile.read())
            pyFile.close()

def generatePoFiles():
    '''Generate the new PO files from the temporary files by different 
    language.
    '''
    for language in languages:
        poFileName = getPoFile(language)
        cmd = 'python pygettext.py -o ' + poFileName + ' ' + tempPyFile
        os.system(cmd)
        print 'Execute command:', cmd
        
        poFile = open(poFileName)
        text = poFile.read()        
        text = text.replace('CHARSET', 'utf-8')
        text = text.replace(u'+\u4e2d\u56fd\u6807\u51c6\u65f6\u95f4', '')
        poFile.close()
        
        poFile = open(poFileName, 'w')
        poFile.write(text)
        poFile.close()
    
    # After all new PO files are generated, delete the python temporary file.
    os.remove(tempPyFile)

def mergeOldMessages():
    '''This method checks the old message dictionary to see if there are some 
    exist translated messages for the new messages of the new PO file. First, 
    we will build new message dictionary from the new PO file, and update the 
    new message dictionary through the old message dictionary. Finally, write 
    the new message dictionary to the PO file.
    '''
   
    for language in languages:
        # Build new message dictionary from the new PO file.
        newMsgDict = buildMsgDict(language)
        
        # Find if there is a new key in the message dictionary, and set the 
        # value of the key in new message dictionary by the value of the key
        # in the old message dictionary.
        for newKey in newMsgDict.keys():
            if newKey in oldMsgDictOfAllLanguages[language].keys():
                print 'Find the key in old message dictionary...'
                newMsgDict[newKey] = oldMsgDictOfAllLanguages[language][newKey]
        
        # Save the new message dictionary to PO file.
        poFileName = getPoFile(language)
        print 'Save merged file:', os.path.abspath(poFileName)
        saveMsgDictToFile(newMsgDict, poFileName)

# The header of the PO file, it will be used to generate PO files.
poHeader = """# PO File Generated by py2po.py script.
# Copyright (C) 2008 Widen
# Jiakuan Wang <delight.wjk@gmail.com>, 2008.
#
"""
    
def saveMsgDictToFile(msgDict, poFileName):
    '''Save the message dictionary to the specified file following the correct
    format.
    '''
    
    # Open the file
    poFile = open(poFileName, 'w')
    
    # Declare a temporary variable to store the content of the file.
    content = poHeader
    keys = msgDict.keys()
    
    # Sort the keys
    keys.sort()
    
    # Deal with keys and values and merge them in content variable.
    for key in keys:
        content = content + 'msgid ' + key
        content = content + 'msgstr ' + msgDict[key] + '\n'
    
    # Write content to file.
    poFile.write(content)
    poFile.close()

if __name__ == '__main__':
    # Check the old PO files.
    print '\n', '-' * 60, '\nCheck the old PO files...\n', '-' * 60
    checkOldPoFiles()
    
    # Merge all python files of the application.
    print '\n', '-' * 60, '\nMerge all PY files to one temporary file...\n', '-' * 60
    mergePyFiles()
    
    # Generate new PO files.
    print '\n', '-' * 60, '\nGenerate new PO files...\n', '-' * 60
    generatePoFiles()
    
    # Merge old messages from the old message dictionaries.
    print '\n', '-' * 60, '\nMerge old messages to the new PO files...\n', '-' * 60
    mergeOldMessages()
